#include <windows.h>
#include "Win32CacheEntry.h"

#define DEFAULT_BUFFER_SIZE	16 * 1024

#ifndef SAFE_DELETE
#define SAFE_DELETE(o) if(o) delete (o); (o) = NULL
#endif

#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(o) if(o) delete[] (o); (o) = NULL
#endif 

LIB_CACHE_NAMESPACE_BEGIN

Win32CacheEntry::Win32CacheEntry(const string& basePath)
{
	m_basePath = basePath;
	m_bufferSize = 0; 
	m_dataSize = 0;
	m_dataPointer = -1;
	m_buffer = NULL;
	m_rwlock = new pthread_rwlock_t;
	pthread_rwlock_init(m_rwlock, NULL);
}

Win32CacheEntry::~Win32CacheEntry(void)
{
	pthread_rwlock_wrlock(m_rwlock);
	SAFE_DELETE_ARRAY(m_buffer);
	m_bufferSize = 0; 
	m_dataSize = 0;
	m_dataPointer = -1;
	pthread_rwlock_unlock(m_rwlock);
	pthread_rwlock_destroy(m_rwlock);
	SAFE_DELETE(m_rwlock);
}

bool Win32CacheEntry::Read(byte* buffer, const size_t& bufferSize, size_t& readSize)
{
	readSize = 0;
	if(buffer == 0)
		return false;
	if(m_dataPointer < 0 || m_dataPointer >= m_dataSize)
		return false;

	pthread_rwlock_rdlock(m_rwlock);
	if(bufferSize + m_dataPointer >= m_dataSize)
		readSize = m_dataSize - m_dataPointer;
	else
		readSize = m_dataSize;

	if(m_dataPointer < 0)
		m_dataPointer = 0;
	m_dataPointer += readSize;
	memcpy(buffer, m_buffer, readSize);
	pthread_rwlock_unlock(m_rwlock);

	return true;
}

bool Win32CacheEntry::Write(const byte* buffer, const size_t& bufferSize, size_t& writeSize)
{
	if(buffer == NULL)
		return false;

	writeSize = 0;
	if(!CanWrite())
		return false;

	pthread_rwlock_wrlock(m_rwlock);
	if(m_buffer == NULL)
	{
		m_buffer = new byte[bufferSize];
		m_dataPointer = 0;
		m_bufferSize = bufferSize;
		m_dataSize = 0;
	}
	if(m_dataPointer + bufferSize >= m_bufferSize)
		writeSize = m_bufferSize - m_dataPointer;
	else
		writeSize = bufferSize;
	memcpy(m_buffer + m_dataPointer, buffer, writeSize);
	m_dataSize += writeSize;
	pthread_rwlock_unlock(m_rwlock);
	return true;
}

bool Win32CacheEntry::GetRawBuffer(byte** buffer, size_t& bufferSize) const
{
	if(buffer == NULL)
		return false;

	*buffer = m_buffer;
	bufferSize = m_dataSize;
	return true;
}

bool Win32CacheEntry::SetRawBuffer(byte* buffer, size_t& bufferSize)
{
	if(buffer == NULL)
		return false;
	pthread_rwlock_wrlock(m_rwlock);
	m_buffer = buffer;
	m_dataSize = bufferSize;
	m_bufferSize = bufferSize;
	m_dataPointer = 0;
	pthread_rwlock_unlock(m_rwlock);
	return true;
}

bool Win32CacheEntry::SeekPointer(int position, int begin)
{
	if(position + begin >= m_dataSize)
		return false;
	m_dataPointer = position + begin;
	return true;
}

bool Win32CacheEntry::PersistData(const char* path, bool absolutePath) const
{
	string filePath;
	if(path == NULL)
		filePath = m_basePath + m_name;
	else if(!absolutePath)
		filePath = m_basePath + path;
	else
		filePath = path;
	HANDLE hFile = CreateFile(filePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
								CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
	if(INVALID_HANDLE_VALUE == hFile)
		return false;
	
	pthread_rwlock_rdlock(m_rwlock);
	DWORD dw = -1;
	bool ret = WriteFile(hFile, m_buffer, m_dataSize, &dw, NULL);
	pthread_rwlock_unlock(m_rwlock);

	FlushFileBuffers(hFile);
	CloseHandle(hFile);
	return ret;
}

bool Win32CacheEntry::LoadPersistData(const char* path, bool absolutePath)
{
	string destPath;
	if(path == NULL)
	{
		destPath = m_basePath + m_name;
	}
	else
	{
		if(absolutePath)
			destPath = path;
		else
			destPath = m_basePath + path;
	}
	
	HANDLE hFile = CreateFile(destPath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
								OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);

	if(INVALID_HANDLE_VALUE == hFile)
		return false;

	size_t size = 0;
	DWORD dwHigh = -1;
	size = GetFileSize(hFile, &dwHigh);
	byte* buffer = new byte[size];
	if(!ReadFile(hFile, buffer, size, (DWORD*)&size, NULL))
	{
		SAFE_DELETE_ARRAY(buffer);
		CloseHandle(hFile);
		return false;
	}
	CloseHandle(hFile);

	pthread_rwlock_wrlock(m_rwlock);
	SAFE_DELETE_ARRAY(m_buffer);
	m_buffer = buffer;
	m_bufferSize = size;
	m_dataSize = size;
	m_dataPointer = 0;
	pthread_rwlock_unlock(m_rwlock);
	return true;
}

size_t Win32CacheEntry::GetSize() const
{
	pthread_rwlock_rdlock(m_rwlock);
	size_t size = m_dataSize;
	pthread_rwlock_unlock(m_rwlock);
	return size;
}

bool Win32CacheEntry::SetSize(const size_t& size)
{
	if(m_bufferSize >= size)
		return false;
	pthread_rwlock_wrlock(m_rwlock);
	byte* newBuffer = new byte[size];
	if(m_buffer != NULL)
	{
		memcpy(newBuffer, m_buffer, m_bufferSize);
		m_dataPointer = m_bufferSize;
		SAFE_DELETE_ARRAY(m_buffer);
	}
	m_buffer = newBuffer;
	m_dataSize = size;
	pthread_rwlock_unlock(m_rwlock);
	return true;
}

const char* Win32CacheEntry::GetName() const
{
	return m_name.c_str();
}

bool Win32CacheEntry::SetName(const char* name)
{
	pthread_rwlock_wrlock(m_rwlock);
	m_name = name;
	pthread_rwlock_unlock(m_rwlock);
	return true;
}

bool Win32CacheEntry::Clone(ICacheEntry** cacheEntry) const
{
	if(cacheEntry == NULL)
		return false;

	pthread_rwlock_rdlock(m_rwlock);
	Win32CacheEntry* cache = new Win32CacheEntry(m_basePath);
	cache->m_buffer = new byte[m_bufferSize];
	memcpy(cache->m_buffer, m_buffer, m_bufferSize);
	cache->m_bufferSize = m_bufferSize;
	cache->m_name = m_name;
	pthread_rwlock_unlock(m_rwlock);
	*cacheEntry = cache;
	return true;
}

bool Win32CacheEntry::Lock(bool read) const
{
	if(read)
		return pthread_rwlock_rdlock(m_rwlock);
	else
		return pthread_rwlock_wrlock(m_rwlock) == 0;
}

bool Win32CacheEntry::UnLock() const
{
	return pthread_rwlock_unlock(m_rwlock) == 0;
}

bool Win32CacheEntry::TryLock(bool read) const
{
	if(read)
		return pthread_rwlock_tryrdlock(m_rwlock);
	else
		return pthread_rwlock_trywrlock(m_rwlock);
}

bool Win32CacheEntry::IsDirty() const
{
	return m_dataPointer < 0;
}

bool Win32CacheEntry::CanWrite() const
{
	return true;
}

LIB_CACHE_NAMESPACE_END
